package cn.com.duiba.millionaire.center.biz.mq.callback;

import cn.com.duiba.consumer.center.api.remoteservice.RemoteConsumerService;
import cn.com.duiba.millionaire.center.api.dto.CreditsConsumeLogDto;
import cn.com.duiba.millionaire.center.biz.service.CreditsConsumeLogService;
import cn.com.duiba.order.center.api.dto.CreditsCallbackMessage;
import cn.com.duiba.thirdparty.api.RemoteNotifyDeveloperService;
import cn.com.duiba.thirdparty.dto.NotifyQueueDto;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.Date;
import java.util.Map;

/**
 * Created by xiaoxuda on 2018/2/12.
 */
@Service
public class CreditsConsumeCallback {
    private Logger logger = LoggerFactory.getLogger(CreditsConsumeCallback.class);

    @Autowired
    private CreditsConsumeLogService creditsConsumeLogService;
    @Autowired
    private RemoteNotifyDeveloperService remoteNotifyDeveloperService;
    @Autowired
    private RemoteConsumerService remoteConsumerService;

    public void handle(String message) {
        try {
             CreditsCallbackMessage creditsCallbackMessage = JSONObject.parseObject(message, CreditsCallbackMessage.class);
            CreditsConsumeLogDto logDto = getConsumeLog(creditsCallbackMessage);
            if (logDto == null) {
                return;
            }
            if (CreditsCallbackMessage.CALLBACK_TYPE_COMPLETED.equals(creditsCallbackMessage.getCallbackType())) {
                onCompleted(creditsCallbackMessage, getConsumeResult(creditsCallbackMessage.getMessage()), logDto);
            } else if (CreditsCallbackMessage.CALLBACK_TYPE_FAILED.equals(creditsCallbackMessage.getCallbackType())) {
                onFailed(creditsCallbackMessage, null, logDto);
            } else if (CreditsCallbackMessage.CALLBACK_TYPE_CANCELLED.equals(creditsCallbackMessage.getCallbackType())) {
                onCanceled(creditsCallbackMessage, logDto);
            }
        } catch (Exception e) {
            logger.info("CreditsConsumeCallback.handle ex, ", e);
        }
    }

    public void onCompleted(CreditsCallbackMessage creditsCallbackMessage, CreditsConsumeResult consumeResult,
                            CreditsConsumeLogDto logDto) {
        if (consumeResult == null) {
            onFailed(creditsCallbackMessage, consumeResult, logDto);
            return;
        }
        if ("ok".equals(consumeResult.getStatus())) {
            onSuccess(creditsCallbackMessage, consumeResult, logDto);
        } else {
            onFailed(creditsCallbackMessage, consumeResult, logDto);
        }
    }

    public void onSuccess(CreditsCallbackMessage creditsCallbackMessage, CreditsConsumeResult consumeResult,
                          CreditsConsumeLogDto logDto) {
        creditsConsumeLogService.updateStatus2Success(logDto.getId(), consumeResult.getBizId());
        if(consumeResult.getCredits() != null) {
            remoteConsumerService.updateCredits(Long.parseLong(creditsCallbackMessage.getConsumerId()), consumeResult.getCredits());
        }
        //发送成功通知
        remoteNotifyDeveloperService.notifyDeveloper(generateNotify(creditsCallbackMessage, consumeResult, null));
    }

    public void onFailed(CreditsCallbackMessage creditsCallbackMessage, CreditsConsumeResult consumeResult,
                         CreditsConsumeLogDto logDto) {
        String error4Developer = null;
        if (CreditsCallbackMessage.CALLBACK_TYPE_FAILED.equals(creditsCallbackMessage.getCallbackType())) {
            error4Developer = creditsCallbackMessage.getMessage();
            creditsConsumeLogService.updateStatus2Fail(logDto.getId(), null, creditsCallbackMessage.getMessage(),
                    error4Developer, "扣积分失败请重试");
        } else if (consumeResult != null) {
            error4Developer = consumeResult.getErrorMessage();
            creditsConsumeLogService.updateStatus2Fail(logDto.getId(), consumeResult.getBizId(),
                    consumeResult.getErrorMessage(), error4Developer, consumeResult.getErrorMessage());
        } else {
            error4Developer = creditsCallbackMessage.getMessage();
            creditsConsumeLogService.updateStatus2Fail(logDto.getId(), null,
                    creditsCallbackMessage.getMessage(), error4Developer, "扣积分失败请重试");
        }
        //发送失败通知
        remoteNotifyDeveloperService.notifyDeveloper(generateNotify(creditsCallbackMessage, consumeResult, error4Developer));
    }

    public void onCanceled(CreditsCallbackMessage creditsCallbackMessage, CreditsConsumeLogDto logDto) {
        creditsConsumeLogService.updateStatus2Fail(logDto.getId(), null, "系统繁忙", "系统繁忙",
                "系统繁忙请重试");
    }

    private CreditsConsumeLogDto getConsumeLog(CreditsCallbackMessage creditsCallbackMessage) {
        String logIdStr = creditsCallbackMessage.getParams().get("logId");
        if (!StringUtils.isNumeric(logIdStr)) {
            return null;
        }
        Long logId = Long.parseLong(logIdStr);
        CreditsConsumeLogDto log = creditsConsumeLogService.findById(logId);
        if (log == null) {
            logger.info("CreditsConsumeCallback log not exist, message={}",
                    JSONObject.toJSONString(creditsCallbackMessage));
        }
        return log;
    }

    private CreditsConsumeResult getConsumeResult(String resultStr) {
        if (StringUtils.isBlank(resultStr)) {
            return null;
        }
        if (!resultStr.contains("status")) {
            logger.info("CreditsConsumeCallback.getConsumeResult fail, not exist status in response, result={}", resultStr);
            return null;
        }
        try {
            return JSONObject.parseObject(resultStr, CreditsConsumeResult.class);
        } catch (Exception e) {
            logger.info("CreditsConsumeCallback.getConsumeResult fail, result={}", resultStr, e);
            return null;
        }
    }

    private NotifyQueueDto generateNotify(CreditsCallbackMessage creditsCallbackMessage,
                                          CreditsConsumeResult consumeResult, String error4Develper){
        NotifyQueueDto notify = new NotifyQueueDto();
        Map<String, String> params = creditsCallbackMessage.getParams();
        notify.setNextTime(new Date());
        notify.setTransfer(params.get("transfer"));
        notify.setConsumerId(Long.parseLong(creditsCallbackMessage.getConsumerId()));
        notify.setPartnerUserId(params.get("partnerUserId"));
        notify.setAppId(Long.parseLong(creditsCallbackMessage.getAppId()));
        notify.setRelationType(creditsCallbackMessage.getRelationType());
        notify.setRelationId(Long.parseLong(creditsCallbackMessage.getRelationId()));
        notify.setDeveloperBizId(consumeResult != null ? consumeResult.getBizId() : null);
        notify.setDuibaOrderNum(params.get("orderNum"));
        notify.setResult(CreditsCallbackMessage.CALLBACK_TYPE_COMPLETED.equals(creditsCallbackMessage.getCallbackType())
                && consumeResult != null && "ok".equals(consumeResult.getStatus()));
        notify.setError4developer(error4Develper);
        return notify;
    }

    public static class CreditsConsumeResult implements Serializable {
        private static final long serialVersionUID = -724424218746764647L;

        private String status;
        private String errorMessage;
        private String bizId;
        private Long credits;

        public String getStatus() {
            return status;
        }

        public void setStatus(String status) {
            this.status = status;
        }

        public String getErrorMessage() {
            return errorMessage;
        }

        public void setErrorMessage(String errorMessage) {
            this.errorMessage = errorMessage;
        }

        public String getBizId() {
            return bizId;
        }

        public void setBizId(String bizId) {
            this.bizId = bizId;
        }

        public Long getCredits() {
            return credits;
        }

        public void setCredits(Long credits) {
            this.credits = credits;
        }
    }
}
